/*
 * ciudad.h
 *
 *  Created on: Jul 23, 2014
 *      Author: diego
 */

#ifndef CIUDAD_H_
#define CIUDAD_H_
#define MAX_STR_LENGTH 256

#include <list>
#include <climits>
#include <string>
#include <fstream>
#include <queue>
#include <iostream>
#include <cstdlib> 
#include <stack>
#include <algorithm>
#include "semaforo.h"
#include "calle.h"
#include "cola_prioridad.h"



int Vehiculo::ctdadVehiculos = 0 ;
int Vehiculo::ctdadAutos = 0 ; 
int Vehiculo::ctdadPolicias = 0 ; 
int Vehiculo::ctdadAmbulancias = 0 ; 

struct CmpSemaforo { 
	bool operator ()(Semaforo * lhs, Semaforo* rhs) const { 
		return (lhs->getCarga() < rhs->getCarga()) ; 
	}
} ;
		 

class Ciudad {
private:
	std::vector<Semaforo *> semaforos;
	std::vector<std::vector<Semaforo*>*> * caminos ;
	std::fstream cfg;
	ColaPrioridad<Semaforo*,CmpSemaforo> * ranking ;
	std::vector<Vehiculo*> * llegaron ;

public:
	Ciudad() {

		std::string linea ;
		caminos = new std::vector<std::vector<Semaforo*>*> () ;
		ranking = new ColaPrioridad<Semaforo*,CmpSemaforo> () ;
		llegaron = new std::vector<Vehiculo*> () ;
		Semaforo * s ;
		char * aux = (char * )  malloc ( sizeof(char)*256 ) ;
		int j = 0 ;

		cfg.open("./cfg_ciudad", std::ifstream::in);
		while (cfg.good()) {
			cfg.getline(aux, MAX_STR_LENGTH, '\n');
			linea.assign(aux);
			std::string nombre = linea.substr(0, linea.find(',', 0));
			std::string tipoCalle = linea.substr(linea.find(',',0)+1,1) ; 
			std::string direccion = linea.substr(linea.find_last_of(',')+1,1) ; 
			int capacidad = _decodificarTipoCalle(tipoCalle) ; 
			if ( j < 8){
				for (size_t i = 0; i < 8; i++) {
					s = new Semaforo(nombre + " y ");
					if (!tipoCalle.compare("C")){ 
						if ( !(i==0 && direccion.compare("E")) && !( i==7 && direccion.compare("O"))){
							s->agregarCalle(nombre,direccion,capacidad);
						}
					} if (!tipoCalle.compare("A")){ 
						if( i>0 ) { 
						s->agregarCalle(nombre,"O",capacidad);
						} 
						if ( i<7 ) {
						s->agregarCalle(nombre,"E",capacidad);
						}
					}
					semaforos.push_back(s);
					ranking->push(s) ;
					if( i ) {
						if ( (!direccion.compare("E") || !tipoCalle.compare("A")) && i<8 ) {
							semaforos.at(i+j*8-1)->enlazar(s,nombre,"E") ; 
						} 
						if ( (!direccion.compare("O") || !tipoCalle.compare("A")) && i>0 ) { 
							s->enlazar(semaforos.at(i+j*8-1),nombre,"O") ;
						}
					}
				}
			} else if (j < 16 && j >=8){
				for ( size_t i = 0 ; i < 8 ; i++ ){
					s = semaforos.at(i*8+j-8) ;
					s->cambiarNombre(nombre) ;
					if (!tipoCalle.compare("C")){ 
						if ( !(i==0 && direccion.compare("S")) && !( i==7 && direccion.compare("N")) ){
							s->agregarCalle(nombre,direccion,capacidad);
						}
					} 
					if (!tipoCalle.compare("A")){ 
						if ( i>0 ){ 
							s->agregarCalle(nombre,"N",capacidad);
						} 
						if ( i<7 ){ 
						s->agregarCalle(nombre,"S",capacidad);
						}
					}
					if ( i ) { 
						if ( (!direccion.compare("S") ||  !tipoCalle.compare("A")) && i<8 ) {
							semaforos.at((i-1)*8+j-8)->enlazar(s,nombre,"S") ;
						} 
						if ( (!direccion.compare("N") ||  !tipoCalle.compare("A")) && i>0 ) { 
							s->enlazar(semaforos.at((i-1)*8+j-8),nombre,"N") ; 
						}
					}
				}
			}
			j++ ;
		} 

	}

	bool insertarVehiculo(std::string nombreSem,std::string nombreCalle,Vehiculo * v) { 
		for ( size_t i = 0 ; i<semaforos.size() ; i++ ) {
			Semaforo * s = semaforos.at(i) ;
			Calle * c ;
			if (!s->getNombre().compare(nombreSem)) {
				if( s->getCalle(nombreCalle,&c) ) {
					c->agregarVehiculo(v) ; 	
					return true ; 
				}	
			}
		}
		return false ; 
	}

	int  _decodificarTipoCalle (std::string tipoCalle) { 
		int r ;
		if ( !tipoCalle.compare("A") ){ 
			r=30  ; 
		} else if ( !tipoCalle.compare("C") ) { 
			r=10 ; 
		} 
		return r ;
	} 

	struct CmpDijkstra { 
		bool operator() (Semaforo* lhs, Semaforo* rhs) const { 
			return lhs->getDistancia() > rhs->getDistancia() ; 
		} 
	} ;

	void dijkstra( ) { 
		std::vector<Semaforo*> * vSemaforos ;
	 	ColaPrioridad<Semaforo*,CmpDijkstra> * colaSemaforos ;
		for( size_t i = 0 ; i<semaforos.size() ; i++ ){ 
			colaSemaforos = new ColaPrioridad<Semaforo*,CmpDijkstra> () ; 
			Semaforo * origen = semaforos.at(i) ;
			Semaforo * actual ;
			for( size_t j = 0 ; j<semaforos.size() ; j++) { 
				actual = semaforos.at(j) ; 
				actual->setDesde(NULL) ;
				if ( origen->getNombre() == actual->getNombre() ){ 
					origen->setDistancia(0) ; 
				} else { 
					actual->setDistancia(INT_MAX) ; 
				} 
				colaSemaforos->push(actual) ; 
			}  
			
			while ( !colaSemaforos->empty() ) { 
				actual = colaSemaforos->top() ;
	//			std::cout << actual->getNombre() << ","  ;
				colaSemaforos->pop() ;
				std::vector<Calle *> * semaforosVecinos = actual->getCalles() ; 
				for ( size_t k = 0 ; k < semaforosVecinos->size() ; k++ ) { 
					Semaforo* vecino = semaforosVecinos->at(k)->getSemaforo() ;
					int suma ;
					if ( actual->getDistancia() != INT_MAX ) {
						suma = actual->getDistancia() + semaforosVecinos->at(k)->getPeso() ;  
					} else { suma = INT_MAX ;
					}
					if ( 	suma < vecino->getDistancia() ) { 
						vecino->setDistancia(suma) ; 
						vecino->setDesde(actual) ;
	//					std::cout<<vecino->getNombre()<<"("<<vecino->getDistancia()<<")"; 
					}
				}

//				std::cout << std::endl ; 
				colaSemaforos->update() ;
			}
			delete (colaSemaforos) ;
			for( size_t j = 0 ;  j<semaforos.size() ; j++ ) { 
				Semaforo * destino = semaforos.at(j) ; 
				//std::cout << destino->getNombre() << std::endl  ;
				if( !destino->getCalles()->size() ) continue ;
				vSemaforos = new std::vector<Semaforo*> () ;
				while( destino->getNombre() != origen->getNombre() && destino->getDesde()!=NULL ) { 
					vSemaforos->insert(vSemaforos->begin(),destino) ; 
					destino = destino->getDesde() ;
				}
				if( destino->getNombre() == origen->getNombre() && destino->getDesde()==NULL ) { 
					vSemaforos->insert(vSemaforos->begin(),destino) ; 
				}
				caminos->push_back(vSemaforos) ;	
				//std::cout<< caminos->size() << std::endl ;
			} 				
		}
	}


	void mover() {
		ranking->update() ;		
		bool flag = true ;
		Semaforo * s = ranking->top() ; 
		std::cout <<"///////////// SACO DE : "<< s->getNombre() << " //////////////" << std::endl ;
		std::vector<Calle *> * calles = s->getCalles() ; 
		for ( size_t i = 0 ; i < calles->size() ; i++ ) { 
			while ( !calles->at(i)->empty() && flag ) { 
				Vehiculo * v = calles->at(i)->sacarVehiculo() ; 
				if ( s->getNombre() == v->getDestino() ) {
					llegaron->push_back(v) ;
					Vehiculo::disminuirCtdadVehiculos() ;
					continue ;
				}
				if ( v == NULL ) continue ;
				for ( size_t j = 0 ; j < caminos->size() ; j++ ) { 
						std::vector<Semaforo*> * camino = caminos->at(j) ;
					if ( camino->front()->getNombre() == s->getNombre() 
						&& camino->back()->getNombre() == v->getDestino() ) { 
							Semaforo * sem2 ;
							Semaforo * sem1 ;
						if ( camino->size() > 2 ) { 
							sem2 = camino->at(2) ; 
						} 
						if (camino->size() > 1) { 	
							sem1 = camino->at(1) ; 
						}
						std::vector<Calle*> * callesSem1 = sem1->getCalles() ; 
						for( size_t k = 0 ; k<callesSem1->size() ; k++ ) { 
							if( camino->size() == 2 || callesSem1->at(k)->getSemaforo()->getNombre() == sem2->getNombre() ) { 
								if ( callesSem1->at(k)->getSemaforo()->getNombre() == s->getNombre() ) {
									continue ; 
								}
								if( !(callesSem1->at(k)->agregarVehiculo(v)) ) { 
									calles->at(i)->agregarVehiculo(v) ; 
									flag = false ;
									break ;
								} 
								if ( camino->size() == 2 ) break ;
							}
						}
					} 
				}
			}
		}
	}
		

	std::vector<std::vector<Semaforo*>*> * getCaminos() { 
			return caminos ; 
	}


	void print() {
		for ( size_t i  = 0 ; i < semaforos.size() ; i++ ) {
			semaforos.at(i)->print() ;
		} 
	} 
};

#endif /* CIUDAD_H_ */
